///////////////////////////////////////////////////////////
// 
//  Sequence.cpp
//
//  Wiicussion
//  HCI 575 Class project
//  Spring 2007
// 
//  Authors:  Jeremy Bennett
//            Adam Faeth
//            Michael Oren
//
///////////////////////////////////////////////////////////


// Local Includes
#include "stdafx.h"
#include "Note.h"
#include "Sequence.h"
#include "MyImage.h"

// SDL Includes
#include <SDL_opengl.h>

// System Includes
#include <stdio.h>
#include <iostream>
#include <algorithm>


bool Sequence::s_bVerbose = false;

Sequence::Sequence( float x, float y, float w, float h, int iInst )
: _uID(),
_fRenderSize(),
_fRenderPos(),
_fColor(),
_iStDraw(0),
_iStPlay(0),
_uiStart(0),
_uiTol(100),
_fTrackPos(),
_fNoteW(125.0f),
_fNoteH(125.0f),
_uiNoteTravelTime(3000),
_bShowPlayLine(true)
{
    _fRenderPos[0] = x;
    _fRenderPos[1] = y;
    _fRenderSize[0] = w;
    _fRenderSize[1] = h;
    
    _fColor[0] = 0.0f;
    _fColor[1] = 0.0f;
    _fColor[2] = 1.0f;
    _fColor[3] = 1.0f;
    
    _fTrackPos = new float[iInst];
    for(int i = 0; i < iInst; i++) {
        _fTrackPos[i] = float(h/(iInst+2) * i + h/(iInst+2) + y);
//        std::cout<< "Track " << i << " height: " << _fTrackPos[i] << std::endl;
    }
    
    //_image.loadImage("sequence-bg.tiff");
}

Sequence::~Sequence()
{
    for(size_t i = 0; i < _vNotes.size(); i++)
        delete _vNotes[i];
    _vNotes.clear();
}

bool 
Sequence::setRendPosition( const float *pPos )
{
    _fRenderPos[0] = pPos[0];
    _fRenderPos[1] = pPos[1];
    return true;
}


bool 
Sequence::renderSequence( MyContext *pContext )
{
    glPushMatrix();
    glLoadIdentity();
    
    glTranslatef(_fRenderPos[0],_fRenderPos[1],0.0f);
    
    // Draw the notes
    for(size_t i = _iStDraw; i < _vNotes.size(); i++) {
        _vNotes[i]->renderNote(pContext);
    }
    
    // Draw the playback line
    if(_bShowPlayLine) {
        float fLineLeft = _fRenderSize[0] / 2.0f - ((float)_uiTol/(float)_uiNoteTravelTime * _fRenderSize[0]);
        float fLineRight = _fRenderSize[0] / 2.0f + ((float)_uiTol/(float)_uiNoteTravelTime * _fRenderSize[0]);
        float sf_lineColor[] = { 0.8f, 0.0f, 0.0f, 0.4f };
        glColor4fv(sf_lineColor);
        glBegin(GL_TRIANGLE_STRIP);
        
            // vertex 0
            glTexCoord2f(0.0f,0.0f);
            glVertex3f(fLineLeft, 0.0f, 0.0f);
            
            // vertex 1
            glTexCoord2f(0.0f,1.0f);
            glVertex3f(fLineLeft, _fRenderSize[1], 0.0f);
            
            // vertex 2
            glTexCoord2f(1.0f,0.0f);
            glVertex3f(fLineRight, 0.0f, 0.0f);
            
            // vertex 3
            glTexCoord2f(1.0f,1.0f);
            glVertex3f(fLineRight, _fRenderSize[1], 0.0f);
        glEnd();
    }
    
    glPopMatrix();
    
    return true;
}


// Move the notes since the last time
bool 
Sequence::postFrame()
{
    // update the _fRenderPos() based on time
    // Notes should know how to posision themselves
    // given a start/stop postion, start/stop time
    unsigned int uiTime = SDL_GetTicks();
    for(size_t i = _iStDraw; i < _vNotes.size(); i++) {
        // if the not returns false, it is offscreen, stop displaying it
        if(!_vNotes[i]->postFrame(uiTime - _uiStart)) {
            _iStDraw = i+1;
        }
    }
    return true;
}


// Adds a note to the sequence
bool 
Sequence::add(Note * pNote)
{
    // if this is the first note, start the sequence time from here
    if(_vNotes.size() == 0) {
        _uiStart = pNote->getTime();
        pNote->setTime(0, _uiNoteTravelTime);
    }
    // otherwise make sure the note matches up
    else {
        pNote->setTime(pNote->getTime() - _uiStart, _uiNoteTravelTime);
    }

    _vNotes.push_back(pNote);
    return true;
}


// Adds a note soley based on the instrument
bool 
Sequence::add(unsigned int uiInst, unsigned int uiTrack) 
{
    Note * pNote = new Note(_fRenderSize[0], _fTrackPos[uiTrack], -1.0f*_fRenderSize[0], 0, _fNoteW, _fNoteH, uiInst, SDL_GetTicks());
    add(pNote);
    return true;
}


// Starts the playback timer
bool 
Sequence::playback()
{
    _uiStart = SDL_GetTicks();
    _iStDraw = 0;
    _iStPlay = 0;
    
    // reset the note matches
    for(size_t i = 0; i < _vNotes.size(); i++) {
        _vNotes[i]->setMatched(false);
    }
    return true;
}


// Sets the tolerance of a potential match
bool 
Sequence::setTol(unsigned int uiTol)
{
    _uiTol = uiTol;
    return true;
}


// matches the note played to a note in the sequence
bool 
Sequence::match(unsigned int iInst)
{
    unsigned int _uiPBOffset = _uiNoteTravelTime/2;


    unsigned int uiTime = SDL_GetTicks() - _uiStart;
    
    bool bMatch = false;
    // start with the first note to be played
    for(size_t i = _iStPlay; i < _vNotes.size(); i++) {
        // TOXIC:  Double check this
        int uiDiff = uiTime - (_vNotes[i]->getTime() + _uiPBOffset);
        // std::cout << "uiDiff = " << uiDiff << std::endl;
        if( (iInst == _vNotes[i]->getInst()) && (abs(uiDiff) < _uiTol) ) {
            _vNotes[i]->setMatched(true);
            _iStPlay = i+1;
            bMatch  = true;
            break;
        }
        
        // the note could be counted as missed if iDiff < 0 here
        // but it should also scroll off-screen soon
    }
    
    return bMatch;
}

bool
Sequence::hasPassed()
{
	unsigned int _uiPBOffset = _uiNoteTravelTime/2 + (_uiTol * 2);

	unsigned int uiTime = SDL_GetTicks() - _uiStart;
    
    bool bMatch = false;
    // start with the first note to be played

	if(_vNotes.size() == 0)
		return false;

    for(size_t i = _iStPlay; i < _vNotes.size(); i++) {
        // TOXIC:  Double check this
        int uiDiff = uiTime - (_vNotes[i]->getTime() + _uiPBOffset);
        // std::cout << "uiDiff = " << uiDiff << std::endl;
        if((abs(uiDiff) < _uiTol)) {
            return true;
        }
        // the note could be counted as missed if iDiff < 0 here
        // but it should also scroll off-screen soon
    }
	return false;
}


// clears the sequence
bool 
Sequence::clear()
{
    // Clear the notes from the sequence
    for(size_t i = 0; i < _vNotes.size(); i++) {
        delete _vNotes[i];
    }
    _vNotes.clear();
    _iStDraw = 0;
    _iStPlay = 0;
    return true;
}
